using System;
using System.Collections.Generic;
using System.Text;

namespace Fated.SaveEngine {
    public static class JSONDeserializer {
        private const char STRINGTERM = '\"';
        private const char ARRAYOPEN = '[';
        private const char ARRAYTERM = ']';
        private const char OBJECTOPEN = '{';
        private const char OBJECTTERM = '}';


        /// <summary>
        /// Provides the parsed Dictionary<string, object> of the inputStr.
        /// </summary>
        /// <param name="inputStr">
        /// The string to be parsed.
        /// </param>
        /// <returns>
        /// The parsed input string in a Dictionary<string, object>.
        /// </returns>
        public static Dictionary<string, object> parseJSON(string inputStr) {
            int i = 0;

            return parseObject(inputStr.Trim(), ref i);
        }


        /// <summary>
        /// This function parses the string inputStr.
        /// </summary>
        /// <param name="inputStr">
        /// The string to be parsed.
        /// </param>
        /// <param name="idx">
        /// The index of what character the functions on.
        /// </param>
        /// <returns>
        /// The parsed input string in a Dictionary<string, object>.
        /// </returns>
        private static Dictionary<string, object> parseObject(string inputStr, ref int idx) {
            Dictionary<string, object> o = new Dictionary<string, object>();
            string indexString = "";


            while (idx++ < inputStr.Length) {

                if (inputStr[idx] == OBJECTTERM) return o;

                if (inputStr[idx] == STRINGTERM && indexString == "") {
                    indexString = parseString(inputStr, ref idx);
                } else if (inputStr[idx] == STRINGTERM && indexString != "") {
                    o.Add(indexString, parseString(inputStr, ref idx));
                    indexString = "";
                } else if ((inputStr[idx] > 47 && inputStr[idx] < 58) || inputStr[idx] == '.') {
                    o.Add(indexString, parseNumber(inputStr, ref idx));
                    indexString = "";
                } else if (inputStr[idx] == ARRAYOPEN) {
                    o.Add(indexString, parseArray(inputStr, ref idx));
                    indexString = "";
                } else if (inputStr[idx] == OBJECTOPEN) {
                    o.Add(indexString, parseObject(inputStr, ref idx));
                    indexString = "";
                }
            }

            return o;
        }


        /// <summary>
        /// Parses the string inputStr into a number
        /// </summary>
        /// <param name="inputStr">
        /// The string to be parsed.
        /// </param>
        /// <param name="idx">
        /// The index of what character the functions on.
        /// </param>
        /// <returns>
        /// This function returns the string inputStr as a double.
        /// </returns>
        private static double parseNumber(string inputStr, ref int idx) {
            double number = 0;
            int postDecPt = 0;

            for (int i = idx; i < inputStr.Length; ++i) {
                idx = i;
                if (inputStr[i] <= 47 || inputStr[i] >= 58) {
                    if (inputStr[i] != '.') {
                        break;
                    } else {
                        continue;
                    }
                }
                if (postDecPt > 0)
                    number += int.Parse(inputStr[i].ToString()) / Math.Pow(10, ++postDecPt);
                else
                    number = number * 10 + int.Parse(inputStr[i].ToString());
            }

            return number;
        }


        /// <summary>
        /// Parses the string inputStr into a string
        /// </summary>
        /// <param name="inputStr">
        /// The string to be parsed.
        /// </param>
        /// <param name="idx">
        /// The index of what character the functions on.
        /// </param>
        /// <returns>
        /// This function returns the string inputStr as a string.
        /// </returns>
        private static string parseString(string inputStr, ref int idx) {
            string newString = "";


            for (idx++; idx < inputStr.Length && inputStr[idx] != STRINGTERM; idx++) {
                newString += inputStr[idx];
            }
            idx++;
            //idx should now be pointing to after the second "


            return newString;
        }


        /// <summary>
        /// Parses the string inputStr into a List<object>.
        /// </summary>
        /// <param name="inputStr">
        /// The string to be parsed.
        /// </param>
        /// <param name="idx">
        /// The index of what character the functions on.
        /// </param>
        /// <returns>
        /// This function returns the string inputStr as a List<object>.
        /// </returns>
        private static List<object> parseArray(string inputStr, ref int idx) {
            List<object> newList = new List<object>();


            for (idx++; idx < inputStr.Length && inputStr[idx] != ARRAYTERM; idx++) {

                if ((inputStr[idx] > 47 && inputStr[idx] < 58) || inputStr[idx] == '.') {
                    newList.Add(parseNumber(inputStr, ref idx));
                } else if (inputStr[idx] == STRINGTERM) {
                    newList.Add(parseString(inputStr, ref idx));
                } else if (inputStr[idx] == ARRAYOPEN) {
                    newList.Add(parseArray(inputStr, ref idx));
                } else if (inputStr[idx] == OBJECTOPEN) {
                    newList.Add(parseObject(inputStr, ref idx));
                }
            }


            return newList;
        }
    }
}
